package net.pulseproject.commons.util;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public final class MidiUtil {
	
	private static final Log LOG = LogFactory.getLog(MidiUtil.class);
	
	private MidiUtil() {
		// utility class should not be instantiated
	}
	
	public static String toString(final ShortMessage message) {
		final StringBuilder sb = new StringBuilder();
		
		sb.
			append("ShortMessage[").
			
			append("status=").append(message.getStatus()).
//			append(", ").
//			append("length=").append(message.getLength()).
			append(", ").
			append("channel=").append(message.getChannel()).
			append(", ").
			append("command=").append(message.getCommand()).
			append(", ").
			append("data1=").append(message.getData1()).
			append(", ").
			append("data2=").append(message.getData2()).
//			append(", ").
//			append("data=").append(Arrays.toString(message.getMessage())).
			
			append("]");
		
		return sb.toString();
	}

	public static ShortMessage deriveMessageByData2(final ShortMessage message, final int newData2) {
		LOG.trace("deriveMessageBy(message, newData2=" + newData2 + ")");
		if(message == null) { throw new IllegalArgumentException("message == null"); }
		
		return MidiUtil.createMessage(message.getCommand(), message.getChannel(), message.getData1(), newData2);
	}

	public static ShortMessage deriveMessageByChannel(final ShortMessage message, final int channel) {
		LOG.trace("deriveMessageByChannel(message, channel=" + channel + ")");
		if(message == null) { throw new IllegalArgumentException("message == null"); }
		
		return MidiUtil.createMessage(message.getCommand(), channel, message.getData1(), message.getData2());
	}

	public static ShortMessage deriveMessageByChannelAndData1(final ShortMessage message, final int channel,
			final int data1) {
		LOG.trace("deriveMessageByChannelAndData1(message, channel=" + channel + ", data1=" + data1 + ")");
		if(message == null) { throw new IllegalArgumentException("message == null"); }
		return MidiUtil.createMessage(message.getCommand(), channel, data1, message.getData2());
	}
	
	public static ShortMessage createMessage(final int command, final int channel, final int data1, final int data2) {
		final ShortMessage message = new ShortMessage();
		try {
			message.setMessage(command, channel, data1, data2);
		} catch (final InvalidMidiDataException e) {
			throw new RuntimeException("Could not create message with paramters: command=" + command + ", " +
					"channel=" + channel + ", data1=" + data1 + ", data2=" + data2 + "", e);
		}
		return message;
	}
	
	public static ShortMessage createMessageByStatus(final int status, final int data1, final int data2) {
		final ShortMessage message = new ShortMessage();
		try {
			message.setMessage(status, data1, data2);
		} catch (final InvalidMidiDataException e) {
			throw new RuntimeException("Could not create message with paramters: status=" + status + ", " +
					"data1=" + data1 + ", data2=" + data2 + "", e);
		}
		return message;
	}
	
	/**
	 * @param receiverPort for debugging (logging and exception message) purpose only. 
	 */
	public static Receiver openReceiver(final String receiverPort, final MidiDevice device) {
		if(receiverPort == null) { throw new IllegalArgumentException("receiverPort == null"); }
		if(receiverPort.isEmpty()) { throw new IllegalArgumentException("receiverPort is empty"); }
		if(device == null) { throw new IllegalArgumentException("device == null"); }
		try {
			LOG.debug("Opening MIDI receiver for port [" + receiverPort + "] ...");
			return device.getReceiver();
		} catch(final MidiUnavailableException e) {
			throw new RuntimeException("Could not open receiver port [" + receiverPort + "] for " +
					"midi device " + device + "!", e);
		}
	}
	
}
